package amazon

import (
	"fmt"
	"gitee.com/wengo/go-mamba"
	http "github.com/Danny-Dasilva/fhttp"
	"github.com/PuerkitoBio/goquery"
	"github.com/peterbourgon/mergemap"
	"net/url"
	"strings"
)

type Base struct {
	client  *mamba.Client
	history []string
}

func (a *Base) SetCookies(u string, cookies []*http.Cookie) {
	uri, _ := url.Parse(u)
	a.client.GetClient().Jar.SetCookies(uri, cookies)
}

// 设置BaseUrl
//
// url: 链接
func (a *Base) SetBaseUrl(urlstr string) {
	a.client.SetBaseURL(urlstr)
}

func (a *Base) request(method Methods, url string, header, data, query map[string]string) (resp *mamba.Response, documennt *goquery.Document, err error) {
	req := a.client.R().EnableTrace()

	if header != nil {
		req.SetHeaders(header)
	}

	if len(a.history) > 0 {
		referer, ok := header["referer"]
		if ok {
			req.SetHeader("Referer", referer)
		} else {
			req.SetHeader("Referer", a.history[len(a.history)-1])
		}
		//req.SetHeader("Referer", a.history[len(a.history) - 1])
	}

	if data != nil {
		req.SetFormData(data)
		_, ok := header["Content-Length"]
		if ok {
			req.SetContentLength(true)
		}
	}

	if query != nil {
		req.SetQueryParams(query)
	}

	switch method {
	case POST:
		resp, err = req.Post(url)
	case GET:
		resp, err = req.Get(url)
	case OPTIONS:
		resp, err = req.Options(url)
	}

	if err != nil {
		return nil, nil, fmt.Errorf("请求失败 => %s", err.Error())
	}

	switch resp.StatusCode() {
	case 404:
		return nil, nil, fmt.Errorf("404")
	}

	documennt, err = a.parseResponse(resp.String())
	if err != nil {
		return nil, nil, fmt.Errorf("解析html失败 => %s", err.Error())
	}

	a.addHistory(url)

	return resp, documennt, err
}

func (a *Base) json(url string, header map[string]string, body interface{}) (resp *mamba.Response, documennt *goquery.Document, err error) {
	req := a.client.R()

	req.SetHeader("content-type", "application/json")

	req.SetHeaders(header)

	req.SetBody(body)

	resp, err = req.Post(url)

	if err != nil {
		return nil, nil, err
	}

	documennt, err = a.parseResponse(resp.String())

	return resp, documennt, err
}

// 添加请求历史记录
//
// method: 请求方式, POST, GET, OPTION, HEAD
//
// url: 网址
//
// header: 请求头部
//
// data: Post内容
//
// query: Url参数
func (a *Base) addHistory(url string) {

	if !strings.Contains(url, "https") {
		url = fmt.Sprintf("%s%s", a.client.BaseURL, url)
	}

	a.history = append(a.history, url)
}

// 处理request返回的数据
//
// Response: Response数据
func (a *Base) parseResponse(html string) (document *goquery.Document, err error) {
	if strings.Contains(html, "opfcaptcha") && !strings.Contains(html, "cvf-page-content") {
		return nil, fmt.Errorf("出现验证码")
	}

	document, err = goquery.NewDocumentFromReader(strings.NewReader(html))

	return
}

// 提取表单数据
//
// document: 当前返回的Response
func (a *Base) ExtractFormData(document *goquery.Document, formId string) (string, Methods, map[string]string, error) {
	var formData = make(map[string]string)

	if document.Find(formId).Length() == 0 {
		return "", "", nil, fmt.Errorf("页面内不存在 %s 的dom", formId)
	}

	action, _ := document.Find(formId).Attr("action")

	methodType, _ := document.Find(formId).Attr("method")
	var method Methods
	methodType = strings.ToLower(methodType)
	if methodType == "post" {
		method = POST
	} else if methodType == "get" {
		method = GET
	}

	radioIndex := 2
	document.Find(fmt.Sprintf("%s input", formId)).Each(func(i int, selection *goquery.Selection) {
		name, _ := selection.Attr("name")
		if name != "" {
			value, _ := selection.Attr("value")
			if _, exists := formData[name]; exists {
				formData[fmt.Sprintf("%s_#radio%d#", name, radioIndex)] = value
				radioIndex++
			} else {
				formData[name] = value
			}
		}
	})

	// 改良版本，还未使用，未经过测试
	//document.Find(fmt.Sprintf("%s input", formId)).Each(func(i int, selection *goquery.Selection) {
	//	name, _ := selection.Attr("name")
	//	if name != "" {
	//		value, _ := selection.Attr("value")
	//		inputType, _ := selection.Attr("type")
	//		_, exists := formData[name]; if exists {
	//			formData[name] = formData[name] + ";" + value
	//		} else {
	//			formData[name] = value
	//		}
	//	}
	//})

	return action, method, formData, nil
}

// 删除style, 删除script
//
// url: 网址
func (a *Base) RemoveStyle(document *goquery.Document) *goquery.Document {
	document.Remove()
	return document
}

// Telnet为并发做准备
//
// url: 网址
func (a *Base) Telnet(urlstr string) {
	_, _, _ = a.request(GET, urlstr, nil, nil, nil)
	return
}

// 浏览某个页面
//
// url: 网址
func (a *Base) Navigate(urlstr string) (resp *mamba.Response, document *goquery.Document, err error) {
	header := map[string]string{"referer": "https://www.amazon.com"}
	resp, document, err = a.request(GET, urlstr, header, nil, nil)

	return
}

// 浏览某个页面
//
// url: 网址
func (a *Base) Ajax(urlstr string, method Methods, header map[string]interface{}, query, formData map[string]string) (resp *mamba.Response, document *goquery.Document, err error) {
	var headers = make(map[string]string)
	mergeHeaders := mergemap.Merge(map[string]interface{}{
		"Accept":           "text/html,*/*",
		"X-Requested-With": "XMLHttpRequest",
	}, header)

	for key, value := range mergeHeaders {
		headers[key] = value.(string)
	}

	resp, document, err = a.request(method, urlstr, headers, formData, query)

	return
}

// 提交表单
//
// url: 网址
func (a *Base) Submit(action string, method Methods, header, query, formData map[string]string) (resp *mamba.Response, document *goquery.Document, err error) {
	if method == POST {
		resp, document, err = a.request(method, action, header, formData, query)
	} else {
		resp, document, err = a.request(method, action, header, nil, query)
	}

	return
}
